package cn.wgx.commons.storage.filetool;

import cn.wgx.commons.util.OSInfo;
import cn.wgx.commons.util.StringUtils;
import com.alibaba.fastjson.JSONObject;
import org.apache.commons.lang3.math.NumberUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.*;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class FFmpegUtil {
    private static Logger logger = LoggerFactory.getLogger(FFmpegUtil.class);

    private static String DEFAULT_FFMPEGFILE_WINDOWS = "ffmpeg";
    private static String DEFAULT_FFMPEGFILE_LINUX = "ffmpeg";

    private static String DEFAULT_FFPROBEFILE_WINDOWS = "ffprobe.exe";
    private static String DEFAULT_FFPROBEFILE_LINUX = "ffprobe";

    private static String DEFAULT_WATERMARK_LOGO = "logo.png";

    private static String FFMPEG_LOCAL_FILENAME = "ffmpeg.exe";
    private static String FFPROBE_LOCAL_FILENAME = "ffprobe.exe";

    private static String FFMPEG_FULL_PATH;
    private static String FFPROBE_FULL_PATH;

    private static String CMD_WORK_PATH = "";
    private static File WORK_PATH ;

    static {
//        String path = FFmpegUtil.class.getClassLoader().getResource("").getPath();
        setCmdWorkPath("");
    }

    public static void setCmdWorkPath(String cmdWorkPath) {
        CMD_WORK_PATH = cmdWorkPath;
        iniPath();
    }

    private static void iniPath(){
        if (OSInfo.isWindows()) {
            FFMPEG_FULL_PATH = CMD_WORK_PATH + DEFAULT_FFMPEGFILE_WINDOWS;
            FFPROBE_FULL_PATH = CMD_WORK_PATH + DEFAULT_FFPROBEFILE_WINDOWS;
        } else {
            FFMPEG_FULL_PATH = DEFAULT_FFMPEGFILE_LINUX;
            FFPROBE_FULL_PATH = DEFAULT_FFPROBEFILE_LINUX;
        }
//        FFMPEG_FULL_PATH = CMD_WORK_PATH + FFMPEG_LOCAL_FILENAME;
//        FFPROBE_FULL_PATH = CMD_WORK_PATH + FFPROBE_LOCAL_FILENAME;
//        DEFAULT_WATERMARK_LOGO = CMD_WORK_PATH.replaceFirst(":", "\\\\:") + "logo.png";

        WORK_PATH = new File(CMD_WORK_PATH);

        logger.info("ffmpeg 路径为: " + FFMPEG_FULL_PATH);
        logger.info("ffprobe 路径为: " + FFPROBE_FULL_PATH);
        logger.info("CMD_WORK_PATH 路径为: " + CMD_WORK_PATH);
    }

    private final static String [] SUPPORT_VIDEO_EXT = {"mp4", "avi","mpeg", "mpg", "mtv", "mov", "3gp"};
    /**
     * 支持的视频后缀
     * @param ext
     * @return
     */
    public static boolean supportVideo(String ext){
        return false;
    }

    private final static String [] SUPPORT_AUDIO_EXT = {"mp3", "m4a", "wav", "wma", "wav", "ogg", "ape", "acc" };

    /**
     * 支持的音频后缀
     * @param ext
     * @return
     */
    public static boolean supportAudio(String ext){
        for(String s : SUPPORT_AUDIO_EXT){
            if(s.equals(s)){
                return true;
            }
        }
        return false;
    }

    public static File[] cutImage(String srcFile, String tmpPath, int time, int n) {
        logger.isDebugEnabled();
        File[] files = new File[n];
        for (int i = 0; i < n; i++) {
            String image = tmpPath + UUID.randomUUID() + ".jpg";
            File iFile = getThumb(srcFile, image, time / n * i);
            if (null != iFile) {
                files[i]=(iFile);
            }
        }
        return files;
    }

    public static File createAudioImg(String src, String targetFile){
        ArrayList<String> cmd = new ArrayList<>();
        cmd.add(FFMPEG_FULL_PATH);
        cmd.add("-v");
        cmd.add("quiet");
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(src);
        cmd.add("-filter_complex");
        cmd.add("\"showwavespic=s=2000x200\"");
        cmd.add("-vframes:v");
        cmd.add("1");
        cmd.add(targetFile);
        exec(cmd, false);
        File f = new File(targetFile);
        if(f.exists()){
            return f;
        }
        return null;
    }

    /****
     *
     * 截图
     * @param src:视频路径
     * @param targetFile:图片保存路径
     * @param sec:指定秒
     * @throws IOException
     * @throws InterruptedException
     */
    public static File getThumb(String src, String targetFile, float sec) {
        ArrayList<String> cmd = new ArrayList<>();
        cmd.add(FFMPEG_FULL_PATH);
        cmd.add("-loglevel");
        cmd.add("quiet");
        cmd.add("-ss");
        cmd.add(sec + "");
        cmd.add("-y");
        cmd.add("-i");
        cmd.add(src);
        cmd.add("-vframes");
        cmd.add("1");
        cmd.add("-f");
        cmd.add("image2");
        cmd.add(targetFile);
        exec(cmd, false);
        File f = new File(targetFile);
        if(f.exists()){
            return f;
        }
        return null;
    }

    /**
     * 截取片段: 视频/音频
     * @param localFile
     * @param newFilePath
     * @param ss 开始位置
     * @param t  持续时间
     * @return
     */
    public static File cut(File localFile, String newFilePath, int ss, int t) {
        ArrayList<String> cmd = new ArrayList<>();
        cmd.add(FFMPEG_FULL_PATH);
        cmd.add("-y");
        cmd.add("-v");
        cmd.add("quiet");
        cmd.add("-ss");
        cmd.add(ss + "");
        cmd.add("-t");
        cmd.add(t + "");
        cmd.add("-i");
        cmd.add(localFile.getAbsolutePath());
        cmd.add("-codec");
        cmd.add("copy");
        newFilePath = StringUtils.getFileNameNoExt(newFilePath) + ".mp4";
        cmd.add(newFilePath);
        exec(cmd, false);
        File f = new File(newFilePath);
        if(f.exists()){
            return f;
        }
        return null;
    }

    /**
     * 截取 并 加水印
     * @param localFile
     * @param newFilePath
     * @param ss
     * @param t
     * @return
     */
    public static File waterMarkAndCut(File localFile, String newFilePath, int ss, int t) {
        ArrayList<String> cmd = new ArrayList<>();
        cmd.add(FFMPEG_FULL_PATH);
        cmd.add("-v");
        cmd.add("quiet");
        cmd.add("-y");
        cmd.add("-ss");
        cmd.add(ss+"");
        if(t >= 0){
            cmd.add("-t");
            cmd.add(t + "");
        }
        cmd.add("-i");
        cmd.add(localFile.getAbsolutePath());
        cmd.add("-vf");
        cmd.add("movie="+DEFAULT_WATERMARK_LOGO+"[logo],[in][logo]overlay=10:10[out]");
        newFilePath = StringUtils.getFileNameNoExt(newFilePath) + ".mp4";
        cmd.add(newFilePath);
        exec(cmd, false);
        File f = new File(newFilePath);
        if(f.exists()){
            return f;
        }
        return null;
    }



    /**
     * 如果返回不是null的值就是成功(值为转换用时单位:秒)
     *
     * @param instr
     * @return
     */
    public static String returnSecond(String instr) {
        String returnValue = null;
        if (null != instr) {
            String[] a = instr.split("\\.");
            String[] b = a[0].split(":");
            int returnNumber = 0;
            if (null != instr && b[0].length() != 0) {
                returnNumber = Integer.valueOf(b[0]) * 60 * 60 + Integer.valueOf(b[1]) * 60 + Integer.valueOf(b[2]);
                returnValue = String.valueOf(returnNumber);
            } else {
                returnValue = null;
            }
        }
        return returnValue;
    }

    /**
     * 执行cmd
     *
     * @param cmd
     * @return 是否需要返回结果
     */
    public static List<String> exec(List<String> cmd, boolean readInfo) {
        logger.debug("执行cmd: " + cmd.toString());
        Process proc = null;
        BufferedReader stdout = null;
        List<String> returnStringList = null;
        try {
            ProcessBuilder builder = new ProcessBuilder();
            //builder.directory(WORK_PATH);
            builder.command(cmd);
            builder.redirectErrorStream(true);
            proc = builder.start();
            if (readInfo) {
                InputStream is = proc.getInputStream();
                new Thread(new Runnable() {
                    public void run() {
                        BufferedReader br = new BufferedReader(new InputStreamReader(is));
                        try {
                            while (br.readLine()!= null) { }
                        } catch (IOException e) {}
                    }
                }).start();
                stdout = new BufferedReader(new InputStreamReader(proc.getErrorStream()));
                String line;
                returnStringList = new LinkedList<>();
                while ((line = stdout.readLine()) != null) {
                    returnStringList.add(dealString(line));
                }
                logger.debug(returnStringList.toString());
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            try {
                if(proc != null){
                    proc.waitFor();
                }
                if(stdout != null){
                    stdout.close();
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return returnStringList;
    }


    public static String dealString(String str) {
        Matcher m = Pattern.compile("^frame=.*").matcher(str);
        String msg = "";
        while (m.find()) {
            msg = m.group();
        }
        return msg;
    }

    /**
     * 如果是数字就是成功的时间(秒数)
     *
     * @param str
     * @return
     */
    public static boolean isNumeric(String str) {
        Pattern pattern = Pattern.compile("[0-9]*");
        Matcher isNum = pattern.matcher(str);
        if (!isNum.matches()) {
            return false;
        }
        return true;
    }

    //ffprobe -v quiet -print_format json -show_format -show_streams filename.mp4
    /**
     * 获取MetaData ,音频/视频
     * @param video_path
     * @return
     */
    public static JSONObject getMetaData(String video_path)  {
        List<String> cmd = new ArrayList<>();
        JSONObject jsonObject = new JSONObject();
        Process p = null;
        BufferedReader br = null;
        cmd.add(FFPROBE_FULL_PATH);
        cmd.add("-v");
        cmd.add("quiet");
        cmd.add("-print_format");
        cmd.add("json");
        cmd.add("-show_format");
        cmd.add("-show_streams");
        cmd.add(video_path);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(cmd);
            p = builder.start();
            //从输入流中读取视频信息
            br = new BufferedReader(new InputStreamReader(p.getInputStream()));
            String line;
            StringBuffer sb = new StringBuffer();
            boolean go = false;
            while ((line = br.readLine()) != null) {
                sb.append(line);
//                if(line.startsWith("Input ")){
//                    go = true;
//                    continue;
//                }
//                if(go){
//                    String []key_value = line.split(":");
//                    if(key_value != null && key_value.length > 1){
//                        StringBuffer sb = new StringBuffer();
//                        for(int i=1;i<key_value.length;i++){
//                            sb.append(key_value[i] + ":");
//                        }
//                        jsonObject.put(key_value[0].trim(), sb.toString().substring(0,sb.length()-1).trim());
//                    }
//                }
            }
            jsonObject = JSONObject.parseObject(sb.toString());
        } catch (Exception e) {
            e.printStackTrace();
        }finally {
            try {
                if(p != null){
                    p.waitFor();
                }
                if(br != null){
                    br.close();
                }
            } catch (IOException e) {
                e.printStackTrace();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
        return jsonObject;
    }

    public static String getVideoSizeByMetaData(JSONObject metaData){
        if(metaData != null){
            try {
                String height = metaData.getJSONArray("streams").getJSONObject(0).getString("height");
                String width = metaData.getJSONArray("streams").getJSONObject(0).getString("width");
                if(height != null && width != null){
                    return width + "x" + height;
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return null;
    }
    public static float getFrameRateByMetaData(JSONObject metaData){
        if(metaData != null){
            try {
                String r_frame_rate = metaData.getJSONArray("streams").getJSONObject(0).getString("r_frame_rate");
                String s1 = r_frame_rate.split("/")[0];
                String s2 = r_frame_rate.split("/")[1];
                if(NumberUtils.isDigits(s1) && NumberUtils.isDigits(s2)){
                    float f1 = Float.valueOf(s1);
                    float f2 = Float.valueOf(s2);
                    if(f1 > f2 && f2 != 0){
                        return (f1 / f2);
                    }
                }
                return  0f;
            } catch (Exception e) {
                e.printStackTrace();
            }
        }
        return 0f;
    }

    private static String regexDuration = "Duration: (.*?), start: (.*?), bitrate: (\\d*) kb\\/s";
    private static Pattern pattern = Pattern.compile(regexDuration);

    /**
     * 获取视频总时间
     *
     * @param video_path 视频路径
     * @return
     */
    public static int getVideoTime(String video_path) {
        List<String> cmd = new ArrayList<>();
        cmd.add(FFMPEG_FULL_PATH);
        cmd.add("-i");
        cmd.add(video_path);
        try {
            ProcessBuilder builder = new ProcessBuilder();
            builder.command(cmd);
            final Process p = builder.start();

            //从输入流中读取视频信息
            BufferedReader br = new BufferedReader(new InputStreamReader(p.getErrorStream()));
            StringBuffer sb = new StringBuffer();
            String line;
            while ((line = br.readLine()) != null) {
                sb.append(line);
            }
            br.close();

            //从视频信息中解析时长
            Matcher m = pattern.matcher(sb.toString());
            if (m.find()) {
                int time = getTimelen(m.group(1));
                logger.debug(video_path + ",视频时长：" + time + ", 开始时间：" + m.group(2) + ",比特率：" + m.group(3) + "kb/s");
                return time;
            }
        } catch (Exception e) {
            e.printStackTrace();
        }

        return 0;
    }



    /**
     * 格式:"00:00:10.68"
     * @param timelen
     * @return
     */
    private static int getTimelen(String timelen) {
        int min = 0;
        String strs[] = timelen.split(":");
        if (strs[0].compareTo("0") > 0) {
            min += Integer.valueOf(strs[0]) * 60 * 60;//小时 * 60 * 60
        }
        if (strs[1].compareTo("0") > 0) {
            min += Integer.valueOf(strs[1]) * 60;//分钟 * 60
        }
        if (strs[2].compareTo("0") > 0) {
            min += Math.round(Float.valueOf(strs[2]));//秒
        }
        return min;
    }

    /**
     * @ HashMap<String,String> cmdMap 参数传递对象<br>
     * dto中包含的参数<br>
     * (必填)1.ffmpeg_path:ffmpeg执行文件地址,如 d:\\ffmpeg\\ffmpeg.exe Linux下直接调用ffmpeg命令(当然你事先已经有这个程序了)<br>
     * (必填)2.input_path:原视频路径<br>
     * (必填)3.video_converted_path:转换后视频输出路径<br>
     * (可选)4.screen_size:视频尺寸 长度乘宽度 乘号用英文小写"x"如 512x480<br>
     * (可选)5.logo:水印地址(其实在ffmpeg中有一个专门的watermark参数,logo跟它有何不同,我还没看,不过对我来说效果一样 貌似需要png图片才行)<br>
     * (可选,如果填写必须有logo才行,默认为0)6.xaxis:水印logo的横坐标(只有logo参数为一个正确路径才行) 比如0<br>
     * (可选,如果填写必须有logo才行,默认为0)6.yaxis:水印logo的纵坐标(只有logo参数为一个正确路径才行) 比如0<br>
     * (可选)vb:视频比特率,传入一个数值,单位在程序里面拼接了k
     * (可选)ab:音频比特率,传入一个数值,单位在程序里面拼接了k
     */
    public void videoTransfer(HashMap<String, String> cmdMap) {
//		   String ffmpeg_path,String input_path,String video_converted_path,String logo,String screen_size,String xaxis,String yaxis,String vb,String ab
        List<String> cmd = new ArrayList<String>();
        cmd.add(cmdMap.get("ffmpeg_path"));

        cmd.add("-ss");
        cmd.add("0");
        cmd.add("-t");
        cmd.add("10");

        cmd.add("-y");
        cmd.add("-i");
        cmd.add(cmdMap.get("input_path"));
        if (null != cmdMap.get("screen_size")) {
            cmd.add("-s");
            cmd.add(cmdMap.get("screen_size"));
        }
        if (null != cmdMap.get("logo")) {
            String logo = cmdMap.get("logo");
            cmd.add("-vf");
            String xaxis = cmdMap.get("xaxis");
            String yaxis = cmdMap.get("yaxis");
            xaxis = xaxis != null && !xaxis.equals("") ? xaxis : "0";
            yaxis = yaxis != null && !yaxis.equals("") ? yaxis : "0";

            String logoString = "movie=" + logo + "[logo],[in][logo]overlay=x=" + xaxis + ":y=" + yaxis + "[out]";
            cmd.add(logoString);
        }
        cmd.add("-strict");
        cmd.add("-2");
        if (null != cmdMap.get("vb") && !cmdMap.get("vb").equals("")) {
            cmd.add("-vb");
            cmd.add(cmdMap.get("vb") + "k");
        }
        if (null != cmdMap.get("ab") && !cmdMap.get("ab").equals("")) {
            cmd.add("-ab");
            cmd.add(cmdMap.get("ab") + "k");
        }
        cmd.add("-q:v");
        cmd.add("4");

        cmd.add(cmdMap.get("video_converted_path"));
        logger.debug("执行命令: " + cmd);
        exec(cmd, false);
    }
}

